/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.interaction3d.assembly.convert;

import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author micha
 */
public class MeshUtil
{
    private static final String SEPARATOR = "!";

    private static final int POSITION = 0, TEXTURE = 1, NORMAL = 2;

    private final double[][] positions, textures, normals;
    private final Map<String, int[][][]> primtiveGroups;


    public MeshUtil(double[][] positions, double[][] textures, double[][] normals)
    {
        if(positions == null || textures == null || normals == null)
        {
            throw new NullPointerException();
        }

        this.positions = positions;
        this.textures = textures;
        this.normals = normals;

        primtiveGroups = new HashMap<String, int[][][]>();
    }   
    
    public void primtives(String material, String type, int[][] positions, int[][] textures, int[][] normals)
    {
        if(positions.length != textures.length || positions.length != normals.length)
        {            
            throw new IllegalArgumentException();
        }

        String group = material + SEPARATOR + type;

        primtiveGroups.put(group, new int[][][] { positions, textures, normals });
    }

    public Mesh create()
    {
        double[][][] coordinates = new double[3][][];
        coordinates[POSITION] = positions;
        coordinates[TEXTURE] = textures;
        coordinates[NORMAL] = normals;

        //weld        
        
        
        for(int i=TEXTURE; i<=NORMAL; i++)
        {
            Weld weld  = new Weld(1e-04);

            for(int[][][] primtiveGroup : primtiveGroups.values())
            {
                int[][] primtives = primtiveGroup[i];
                for(int[] primitive : primtives)
                {
                    for(int j=0; j<primitive.length; j++)
                    {
                        int index = primitive[j];
                        if(index >= 0)
                        {
                            primitive[j] = weld.apply(coordinates[i][index]);
                        }
                    }
                }
            }
            coordinates[i] = weld.coordinates();
        }

        boolean hasTexture = false;
        Smooth smooth = new Smooth(positions.length);
        for(int[][][] primtiveGroup : primtiveGroups.values())
        {
            int numIndices = primtiveGroup[POSITION].length;
            for(int i=0; i<numIndices; i++)
            {
                int[] pos    = primtiveGroup[POSITION][i];
                int[] tex    = primtiveGroup[TEXTURE][i];
                int[] normal = primtiveGroup[NORMAL][i];

                int numElements = pos.length;
                for(int j=0; j<numElements; j++)
                {
                    if(tex[j] >= 0)
                    {
                        hasTexture = true;
                    }
                    pos[j] = smooth.apply(pos[j], normal[j]);
                }
            }
        }

        //TODO: fine until here
        Vertices vertices = new Vertices();
        Mesh.PrimitiveGroup[] primitiveGroups = new Mesh.PrimitiveGroup[primtiveGroups.size()];
        {
            int index = 0;
            for(Map.Entry<String,int[][][]> entry : primtiveGroups.entrySet())
            {
                String material,  primtiveType;
                {
                    String materialGroup = entry.getKey();
                    int seperator = materialGroup.lastIndexOf(SEPARATOR);
                    material = materialGroup.substring(0, seperator);
                    primtiveType = materialGroup.substring(seperator+SEPARATOR.length());
                }
                int[][][] indices = entry.getValue();

                int numPrimtives = indices[POSITION].length;
                int[][] primitives = new int[numPrimtives][];                
                for(int i=0; i<numPrimtives; i++)
                {
                    int numElements = indices[POSITION][i].length;
                    primitives[i] = new int[numElements];
                    for(int j=0; j<numElements; j++)
                    {
                        int[] vertex = hasTexture
                                     ? new int[] { indices[POSITION][i][j], indices[TEXTURE][i][j] }
                                     : new int[] { indices[POSITION][i][j] };

                        int v = vertices.apply(vertex);
                        primitives[i][j] = v;
                    }
                    
                }
                primitiveGroups[index++] = new Mesh.PrimitiveGroup(material, primtiveType, primitives);
            }
        }

        int[][] maps = vertices.attributes();
        int[] remap = Vertices.sort(maps, POSITION);
        for(Mesh.PrimitiveGroup primitiveGroup : primitiveGroups)
        {
            for(int[] p : primitiveGroup.primitives  )
            {
                for(int i=0; i<p.length; i++)
                {
                    p[i] = remap[p[i]];
                }
            }
        }

        maps = Vertices.transpose(maps);
        int[] mapIndices = new int[maps.length];
        maps = Vertices.reduce(maps, mapIndices);

        Mesh.VertexElement[] elements = new Mesh.VertexElement[mapIndices.length];
        elements[POSITION] = new Mesh.VertexElement("POSITION", coordinates[POSITION], smooth.split(), mapIndices[POSITION]);
        if(elements.length > 1)
        {
            elements[TEXTURE] = new Mesh.VertexElement("TEXTURE", coordinates[TEXTURE], mapIndices[TEXTURE]);
        }


        Mesh mesh = new Mesh(elements, maps, primitiveGroups);
        return mesh;
    }

}
